package com.tsfyun.scm.support.singlewindow;

import cn.hutool.core.collection.CollUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.tsfyun.common.base.dto.DeclareBizSendDTO;
import com.tsfyun.common.base.dto.ExportFileDTO;
import com.tsfyun.common.base.enums.BillTypeEnum;
import com.tsfyun.common.base.enums.ManifestTypeEnum;
import com.tsfyun.common.base.exception.ServiceException;
import com.tsfyun.common.base.util.FileUtil;
import com.tsfyun.common.base.util.LocalDateTimeUtils;
import com.tsfyun.common.base.util.StringUtils;
import com.tsfyun.common.base.util.XmlUtils;
import com.tsfyun.scm.dto.declaration.singlewindow.manifest.apply.*;
import com.tsfyun.scm.service.file.IExportFileService;
import com.tsfyun.scm.service.system.ISubjectOverseasService;
import com.tsfyun.scm.service.system.ISubjectService;
import com.tsfyun.scm.vo.customs.DeclarationMemberVO;
import com.tsfyun.scm.vo.customs.DeclarationVO;
import com.tsfyun.scm.vo.customs.singlewindow.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @Description: 舱单对接单一窗口服务类
 * @CreateDate: Created in 2020/12/25 16:57
 */
@RefreshScope
@Slf4j
@Component
public class ManifestBiz {

    @Autowired
    private IExportFileService exportFileService;

    @Value(value = "${file.directory}")
    private String fileDirectory;

    private static final String BUSINESS_TYPE_PATH = "single_window";
    private static final String MANIFEST_TEMP_SAVE = "declaretion_temp_save";

    /**
     * 根据报关单生产舱单实体
     * @param declarationMain
     * @param declarationMembers
     * @return
     */
    public ManifestPlusVO generateHighway(DeclarationVO declarationMain, List<DeclarationMemberVO> declarationMembers){
        ManifestTypeEnum manifestTypeEnum = Objects.equals(BillTypeEnum.IMP.getCode(),declarationMain.getBillType()) ? ManifestTypeEnum.MT1401 : ManifestTypeEnum.MT2401;
        ManifestVO manifest = new ManifestVO();
        manifest.setId(declarationMain.getManifestId());
        manifest.setTypeId(manifestTypeEnum.getCode());
        manifest.setTypeName(manifestTypeEnum.getName());
        manifest.setVoyageNo(declarationMain.getCusVoyageNo());
        manifest.setTrafModeCode("3");
        manifest.setTrafMode("公路运输  road transport");
        manifest.setCustomsCode(declarationMain.getIePortCode());
        manifest.setCustomsCodeName(declarationMain.getIePortName());
        manifest.setLoadingLocationCode(declarationMain.getIePortCode());
        manifest.setLoadingDate(LocalDateTime.now().withHour(0).withMinute(0).withSecond(0).withNano(0));
        manifest.setArrivalDate(declarationMain.getImportDate());
        manifest.setCustomMaster(declarationMain.getCustomMasterCode());
        manifest.setCustomMasterName(declarationMain.getCustomMasterName());
        manifest.setUnitCode(declarationMain.getAgentScc());
        manifest.setMsgRepName(declarationMain.getAgentName());
        manifest.setCustomerId(declarationMain.getCustomerId());
        manifest.setDocNo(declarationMain.getDocNo());

        //保存提运单信息（只有1条）
        List<MftBillVO> mftBillVOList = Lists.newArrayList();
        MftBillVO mftBill = new MftBillVO();
        mftBill.setListNo(1);
        mftBill.setBillNo(declarationMain.getBillNo());
        mftBill.setPaymentTypeCode("1");
        mftBill.setPaymentType("Direct payment");
        mftBill.setGoodsCustomsStatCode("RD01");
        mftBill.setGoodsCustomsStat("公路口岸直通");
        mftBill.setTransLocationIdCode("");
        mftBill.setTransLocationIdName("");
        mftBill.setPackNum(declarationMain.getPackNo());
        mftBill.setPackTypeCode("CT");
        mftBill.setPackType("纸板箱");
        mftBill.setGrossWt(declarationMain.getGrossWt());

        //报关单明细汇总总价
        mftBill.setGoodsValue(BigDecimal.ZERO);
        mftBill.setCurrencyTypeCode(null);
        mftBill.setCurrencyType(null);
        if(CollUtil.isNotEmpty(declarationMembers)) {
            mftBill.setGoodsValue(declarationMembers.stream().map(DeclarationMemberVO::getTotalPrice).reduce(BigDecimal.ZERO,BigDecimal::add).setScale(2,BigDecimal.ROUND_HALF_UP));
            mftBill.setCurrencyTypeCode(declarationMembers.get(0).getCurrencyId());
            mftBill.setCurrencyType(declarationMembers.get(0).getCurrencyName());
        }

        //此处注意进出口
        if(Objects.equals(BillTypeEnum.IMP.getCode(),declarationMain.getBillType())) {
            //收货人名称
            mftBill.setConsigneeName(declarationMain.getRcvgdTradeName());
            //发货人名称
            mftBill.setConsignorName(declarationMain.getConsignorName());
        } else {
           //收货人名称
            mftBill.setConsigneeName(declarationMain.getConsignorName());
            //发货人名称
            mftBill.setConsignorName(declarationMain.getRcvgdTradeName());
        }
        mftBillVOList.add(mftBill);

        //商品项信息（只有1条)
        List<MtfGoodVO> mtfGoodVOList = Lists.newArrayList();
        MtfGoodVO good = new MtfGoodVO();
        good.setBillNo(mftBill.getBillNo());
        good.setListNo(1);
        good.setGoodsSeqNo(1);
        good.setGoodsPackNum(declarationMain.getPackNo());
        good.setGoodsPackTypeCode(mftBill.getPackTypeCode());
        good.setGoodsPackType(mftBill.getPackType());
        good.setGoodsGrossWt(mftBill.getGrossWt());
        good.setGoodsBriefDesc("");
        if(CollUtil.isNotEmpty(declarationMembers)) {
            good.setGoodsBriefDesc(declarationMembers.get(0).getName());
        }
        mtfGoodVOList.add(good);

        //集装箱信息
        List<MftContainerVO> mftContainerVOList = Lists.newArrayList();
        String containerNo = declarationMain.getContainerNo();
        if(StringUtils.isNotEmpty(containerNo)) {
            MftContainerVO mftContainer = new MftContainerVO();
            mftContainer.setBillNo(mftBill.getBillNo());
            mftContainer.setContaSeqNo(1);
            mftContainer.setContaId(containerNo);

            mftContainerVOList.add(mftContainer);
        }
        return new ManifestPlusVO(manifest,mftBillVOList,mtfGoodVOList,mftContainerVOList);
    }


    /**
     * 根据报关单数据生成舱单数据发送单一窗口
     * @param manifestPlusVO
     */
    public DeclareBizSendDTO sen2SingleWindowHighWay(String tenant,ManifestPlusVO manifestPlusVO){
        Manifest manifest = new Manifest();
        ManifestVO manifestVO = manifestPlusVO.getManifestVO();
        ManifestTypeEnum manifestTypeEnum = ManifestTypeEnum.of(manifestVO.getTypeId());
        //报文头信息
        Head head = new Head();
        head.setMessageId(String.format("%s_%s",manifestVO.getId(),StringUtils.UUID()))//报文编号
                .setFunctionCode("9")//报文功能代码 （9：原始/预配舱单传输 3：删除 5：修改）
                .setMessageType(manifestTypeEnum.getCode())//报文类型代码
                .setSenderId("5300078022621_DXPESW0000220050")//发送方代码 4位备案关区代码+9位企业组织机构代码+50位自定义扩展字符
                .setReceiverId("EPORT")//接收方代码  接收报文的4位关区号，或中国电子口岸代码（EPORT）以及信息中心（0000）
                .setSendTime(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS").format(LocalDateTime.now()))//发送时间
                .setVersion("1.0");//版本
        manifest.setHead(head);

        //报文体信息
        com.tsfyun.scm.dto.declaration.singlewindow.manifest.apply.Declaration declaration = new com.tsfyun.scm.dto.declaration.singlewindow.manifest.apply.Declaration();
        declaration.setDeclarationOfficeID(manifestVO.getCustomsCode())//进出境口岸海关代码
                .setId(manifestVO.getVoyageNo());//货物运输批次号

        if(StringUtils.isNotEmpty(manifestVO.getMemo())){
            //备注
            com.tsfyun.scm.dto.declaration.singlewindow.manifest.apply.AdditionalInformation additionalInformation = new com.tsfyun.scm.dto.declaration.singlewindow.manifest.apply.AdditionalInformation();
            additionalInformation.setContent(StringUtils.removeSpecialSymbol(manifestVO.getMemo()));
            declaration.setAdditionalInformation(additionalInformation);
        }

        //运输工具代理企业代码
        //（允许字母与数字组合，如果存在节点最小1位，最大长度18位字符）
        //4位关区号+9位组织机构代码
        if(StringUtils.isNotEmpty(manifestVO.getTransAgentCode())){
            com.tsfyun.scm.dto.declaration.singlewindow.manifest.apply.Agent agent = new com.tsfyun.scm.dto.declaration.singlewindow.manifest.apply.Agent();
            agent.setId(StringUtils.removeSpecialSymbol(manifestVO.getTransAgentCode()));
            declaration.setAgent(agent);
        }

        //运输工具数据(运输方式代码)
        com.tsfyun.scm.dto.declaration.singlewindow.manifest.apply.BorderTransportMeans borderTransportMeans = new com.tsfyun.scm.dto.declaration.singlewindow.manifest.apply.BorderTransportMeans();
        borderTransportMeans.setTypeCode("3");
        declaration.setBorderTransportMeans(borderTransportMeans);

        //承运人代码
        //由字母、数字或“-”、“/”，“_”、半角空格构成，特殊字符最多出现一次且不能作为开头结尾
        if(StringUtils.isNotEmpty(manifestVO.getCarrierCode())){
            com.tsfyun.scm.dto.declaration.singlewindow.manifest.apply.Carrier carrier = new com.tsfyun.scm.dto.declaration.singlewindow.manifest.apply.Carrier();
            carrier.setId(StringUtils.removeSpecialSymbol(manifestVO.getCarrierCode()));
            declaration.setCarrier(carrier);
        }
        List<MftBillVO> mftBillVOList = manifestPlusVO.getMftBillVOList();
        List<MtfGoodVO> mtfGoodVOList = manifestPlusVO.getMtfGoodVOList();
        Map<String,List<MtfGoodVO>> mtfGoodsMap = Maps.newLinkedHashMap();
        if(CollUtil.isNotEmpty(mtfGoodVOList)){
            mtfGoodsMap = mtfGoodVOList.stream().collect(Collectors.groupingBy(MtfGoodVO::getBillNo));
        }
        //集装箱信息
        List<MftContainerVO> mftContainerVOList = manifestPlusVO.getMftContainerVOList();
        Map<String,List<MftContainerVO>> mftContainerMap = Maps.newLinkedHashMap();
        if(CollUtil.isNotEmpty(mftContainerVOList)){
            mftContainerMap = mftContainerVOList.stream().collect(Collectors.groupingBy(MftContainerVO::getBillNo));
        }
        List<Consignment> consignments = declaration.getConsignment();
        for(MftBillVO mbVo : mftBillVOList){
            Consignment consignment = new Consignment();
            if(Objects.nonNull(mbVo.getVolume())){//货物体检
                consignment.setGrossVolumeMeasure(new GrossVolumeMeasure("MTQ",mbVo.getVolume().toString()));//货物体积
            }
            consignment.setTotalPackageQuantity(new TotalPackageQuantity("CT",mbVo.getPackNum().toString()))//货物总件数
                    .setValueAmount(new ValueAmount(mbVo.getCurrencyTypeCode(),mbVo.getGoodsValue().toString()))//货物价值
                    .setConsignee(new Consignee(mbVo.getConsigneeName()));

            consignment.setConsignor(new Consignor(mbVo.getConsignorName()))//发货人
                    .setFreight(new Freight(mbVo.getPaymentTypeCode())) //运费支付信息
                    .setGovernmentProcedure(new GovernmentProcedure(mbVo.getGoodsCustomsStatCode()))//海关货物通关代码信息
                    .setTransportContractDocument(new TransportContractDocument(mbVo.getBillNo()));//提(运)单号

            if(Objects.nonNull(mbVo.getGrossWt())){
                consignment.setGovernmentAgencyGoodsItem(new GovernmentAgencyGoodsItem(new GoodsMeasure(new GrossMassMeasure("KGM",mbVo.getGrossWt().toString())))); //货物总毛重(KG)
            }

//            //途经国家、地区代码
//            BorderTransportMeans borderTransportMeansCountry = new BorderTransportMeans();
//            List<Itinerary> itinerarys = new ArrayList<>();
//            //途经国家为2个字符
//            itinerarys.add(new Itinerary("UA"));
//            borderTransportMeansCountry.setItinerary(itinerarys);
//            consignment.setBorderTransportMeans(borderTransportMeansCountry);

            //商品项信息
            List<ConsignmentItem> consignmentItems = consignment.getConsignmentItem();
            List<MtfGoodVO> itemMtfGoodList = mtfGoodsMap.get(mbVo.getBillNo());
            if(CollUtil.isNotEmpty(itemMtfGoodList)){
                itemMtfGoodList.stream().forEach(mtfGood ->{
                    ConsignmentItem consignmentItem = new ConsignmentItem();
                    Commodity commodity = new Commodity();
                    commodity.setCargoDescription(mtfGood.getGoodsBriefDesc());//商品项简要信息描述
                    if(StringUtils.isNotEmpty(mtfGood.getUndgNo())){
                        commodity.setClassification(new Classification(StringUtils.removeSpecialSymbol(mtfGood.getUndgNo())));
                    }
                    if(StringUtils.isNotEmpty(mtfGood.getHsCode()) && mtfGood.getHsCode().length() == 10){
                        commodity.setHsCode(StringUtils.removeSpecialSymbol(mtfGood.getHsCode()));
                    }
                    if(StringUtils.isNotEmpty(mtfGood.getGoodsDetailDesc())){
                        commodity.setDescription(StringUtils.removeSpecialSymbol(mtfGood.getGoodsDetailDesc()));
                    }

                    consignmentItem.setSequenceNumeric(mtfGood.getGoodsSeqNo().toString())//序号
                            .setCommodity(commodity)
                            .setGoodsMeasure(new GoodsMeasure(new GrossMassMeasure("KGM",mtfGood.getGoodsGrossWt().toString()))) //货物总毛重/商品项毛重
                            .setPackaging(new Packaging(new QuantityQuantity(mtfGood.getGoodsPackTypeCode(),mtfGood.getGoodsPackNum().toString())));//商品项件数
                    consignmentItems.add(consignmentItem);
                });
            }
            consignment.setConsignmentItem(consignmentItems);

            //集装箱信息
            List<TransportEquipment> transportEquipments = consignment.getTransportEquipment();
            List<MftContainerVO> itemMftContainerList = mftContainerMap.get(mbVo.getBillNo());
            if(CollUtil.isNotEmpty(itemMftContainerList)){
                itemMftContainerList.stream().forEach(mftContainer ->{
                    TransportEquipment transportEquipment = new TransportEquipment(mftContainer.getContaId());
                    transportEquipments.add(transportEquipment);
                    consignment.setTransportEquipment(transportEquipments);
                });
            }
            consignment.setTransportEquipment(transportEquipments);

            consignments.add(consignment);
        }


        declaration.setConsignment(consignments).setRepresentativePerson(new RepresentativePerson(manifestVO.getMsgRepName()));//舱单传输人名称


        if("MT1401".equals(manifestVO.getTypeId())){
            //进口原始舱单
            UnloadingLocation unloadingLocation = new UnloadingLocation();
            unloadingLocation.setId(manifestVO.getLoadingLocationCode());

//            if(Objects.nonNull(mtf.getArrivalDate())){
//                unloadingLocation.setArrivalDateTime(LocalDateTimeUtils.formatTime(mtf.getArrivalDate(),"yyyyMMdd"));
//            }

            declaration.setUnloadingLocation(unloadingLocation);//卸货地数据
            //货物装载运输工具时间
            LoadingLocation loadingLocation = new LoadingLocation();
            loadingLocation.setLoadingDateTime(LocalDateTimeUtils.formatTime(manifestVO.getLoadingDate(),"yyyyMMddHHmmss"));
            declaration.setLoadingLocation(loadingLocation);
        }else if("MT2401".equals(manifestVO.getTypeId())){
            //出口预配舱单
            declaration.setLoadingLocation(new LoadingLocation(manifestVO.getLoadingLocationCode(),LocalDateTimeUtils.formatTime(manifestVO.getLoadingDate(),"yyyyMMddHHmmss")));//装货地代码
        }

        manifest.setDeclaration(declaration);

        String outputFileName = String.format("CN_%s_1P0_%s_%s",manifestVO.getTypeId(),head.getSenderId(),head.getSendTime());

        //生成本地临时目录-压缩后删除
        String path = fileDirectory + String.format("%s/%s/%s/%s/%s", tenant,BUSINESS_TYPE_PATH,MANIFEST_TEMP_SAVE,manifestTypeEnum.getCode(),StringUtils.UUID());
        log.info(path);
        //生成报文
        XmlUtils.convertToXmlWithReplace(manifest,path, outputFileName,manifestVO.getTypeId());

        //将生成的文件转移到单一窗口扫描目录，并压缩
        String zipPath = fileDirectory + String.format("%s/%s/%s/%s/", tenant,BUSINESS_TYPE_PATH,MANIFEST_TEMP_SAVE,manifestTypeEnum.getCode());
        String sysFileName = outputFileName + ".zip";
        try {
            FileUtil.execute(path,zipPath + sysFileName);
            log.info("舱单【{}/{}】压缩xml报文文件完成",manifestVO.getId(),manifestVO.getDocNo());
        } catch (IOException e) {
            log.error("压缩舱单报文文件异常",e);
            throw new ServiceException("压缩报文文件异常，请稍后再试");
        }
        //删除原临时文件
        log.info("舱单【{}/{}】删除原始文件完成",manifestVO.getId(),manifestVO.getDocNo());
        FileUtil.deleteDir(new File(path));

        //写入导出下载
        ExportFileDTO exportFileDTO = new ExportFileDTO();
        exportFileDTO.setPath(zipPath + sysFileName);
        exportFileDTO.setFileName(sysFileName);
        exportFileDTO.setOnce(Boolean.TRUE);
        exportFileDTO.setOperator("系统自动生成");

        Long fileId = exportFileService.save(exportFileDTO);

        DeclareBizSendDTO sendDTO = new DeclareBizSendDTO();
        sendDTO.setTenant(manifestVO.getCustomerId().toString());
        sendDTO.setType("manifest");
        sendDTO.setDownUrl("http://159.75.229.63:9099/scm/export/down?id="+fileId);
        sendDTO.setFileName(sysFileName);
        return sendDTO;
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public DeclareBizSendDTO manifestTempRemove(String tenant,ManifestPlusVO manifestPlusVO){
        ManifestVO manifestVO = manifestPlusVO.getManifestVO();
        Manifest manifest = new Manifest();
        ManifestTypeEnum manifestTypeEnum = ManifestTypeEnum.of(manifestVO.getTypeId());
        //报文头信息
        Head head = new Head();
        head.setMessageId(String.format("%s_%s",manifestVO.getId(),StringUtils.UUID()))//报文编号
                .setFunctionCode("3")//报文功能代码 （9：原始/预配舱单传输 3：删除 5：修改）
                .setMessageType(manifestVO.getTypeId())//报文类型代码
                .setSenderId("5300078022621_DXPESW0000220050")//发送方代码 4位备案关区代码+9位企业组织机构代码+50位自定义扩展字符
                .setReceiverId("EPORT")//接收方代码  接收报文的4位关区号，或中国电子口岸代码（EPORT）以及信息中心（0000）
                .setSendTime(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS").format(LocalDateTime.now()))//发送时间
                .setVersion("1.0");//版本
        manifest.setHead(head);

        //报文体信息

        //报文体信息
        Declaration declaration = new Declaration();
        declaration.setDeclarationOfficeID(manifestVO.getCustomsCode())//进出境口岸海关代码
                .setId(manifestVO.getVoyageNo());//货物运输批次号

        //运输工具数据(运输方式代码)
        BorderTransportMeans borderTransportMeans = new BorderTransportMeans();
        borderTransportMeans.setTypeCode(manifestVO.getTrafModeCode());
        declaration.setBorderTransportMeans(borderTransportMeans);

        //提运单信息
        List<MftBillVO> mftBillVOList = manifestPlusVO.getMftBillVOList();
        List<Consignment> consignments = declaration.getConsignment();
        for(MftBillVO mbVo : mftBillVOList){
            Consignment consignment = new Consignment();
            //根据提运单号删除
            consignment.setTransportContractDocument(new TransportContractDocument(mbVo.getBillNo(),new Amendment(new ChangeInformation("填写错误","林某某","13888888888"),"001"))); //运输合同信息
            consignments.add(consignment);
        }

        declaration.setConsignment(consignments).setRepresentativePerson(new RepresentativePerson(manifestVO.getMsgRepName()));//舱单传输人名称

        manifest.setDeclaration(declaration);

        String outputFileName = String.format("CN_%s_1P0_%s_%s",manifestVO.getTypeId(),head.getSenderId(),head.getSendTime());

        //生成本地临时目录-压缩后删除
        String path = fileDirectory + String.format("%s/%s/%s/%s%s", tenant,BUSINESS_TYPE_PATH,MANIFEST_TEMP_SAVE, manifestTypeEnum.getCode(),StringUtils.UUID());
        log.info(path);
        //生成报文
        XmlUtils.convertToXmlWithReplace(manifest,path, outputFileName,manifestVO.getTypeId());
        //将生成的文件转移到单一窗口扫描目录，并压缩
        String zipPath = fileDirectory + String.format("%s/%s/%s/%s/", tenant,BUSINESS_TYPE_PATH,MANIFEST_TEMP_SAVE,manifestTypeEnum.getCode());
        String sysFileName = outputFileName + ".zip";
        try {
            FileUtil.execute(path,zipPath + sysFileName);
            log.info("舱单【{}/{}】压缩xml报文文件完成",manifestVO.getId(),manifestVO.getDocNo());
        } catch (IOException e) {
            log.error("压缩舱单报文文件异常",e);
            throw new ServiceException("压缩报文文件异常，请稍后再试");
        }
        //删除原临时文件
        log.info("舱单【{}/{}】删除原始文件完成",manifestVO.getId(),manifestVO.getDocNo());
        FileUtil.deleteDir(new File(path));

        //写入导出下载
        ExportFileDTO exportFileDTO = new ExportFileDTO();
        exportFileDTO.setPath(zipPath + sysFileName);
        exportFileDTO.setFileName(sysFileName);
        exportFileDTO.setOnce(Boolean.TRUE);
        exportFileDTO.setOperator("系统自动生成");

        Long fileId = exportFileService.save(exportFileDTO);

        DeclareBizSendDTO sendDTO = new DeclareBizSendDTO();
        sendDTO.setTenant(manifestVO.getCustomerId().toString());
        sendDTO.setType("manifest");
        sendDTO.setDownUrl("http://159.75.229.63:9099/scm/export/down?id="+fileId);
        sendDTO.setFileName(sysFileName);

        return sendDTO;
    }


}
